package openjdk.concurrent;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
 * Java中可通过volatile在一定程序上保证顺序性，另外还可以通过synchronized和锁来保证顺序性。
 * <p>
 * synchronized和锁保证顺序性的原理和保证原子性一样，都是通过保证同一时间只会有一个线程执行目标代码段来实现的。
 */
public class LockDemo {

    /**
     * 常用的保证Java操作原子性的工具是锁和同步方法（或者同步代码块）。
     * 使用锁，可以保证同一时间只有一个线程能拿到锁，
     * 也就保证了同一时间只有一个线程能执行申请锁和释放锁之间的代码。
     */
    Lock lock = new ReentrantLock();
    int i = 3;

    public void testLock() {
        lock.lock();
        try {
            int j = i;
            i = j + 1;
        } finally {
            lock.unlock();
        }
    }


    /**
     * 与锁类似的是同步方法或者同步代码块。使用非静态同步方法时，锁住的是当前实例；
     * 使用静态同步方法时，锁住的是该类的Class对象；
     * 使用静态代码块时，锁住的是synchronized关键字后面括号内的对象。
     */
    public void testLock2() {
        synchronized (lock) {
            int j = i;
            i = j + 1;
        }
    }

    /**
     * 基础类型变量自增（i++）是一种常被新手误以为是原子操作而实际不是的操作。
     * Java中提供了对应的原子操作类来实现该操作，并保证原子性，其本质是利用了CPU级别的CAS指令。
     * 由于是CPU级别的指令，其开销比需要操作系统参与的锁的开销小。AtomicInteger使用方法如下
     */
    public void test3() {
        int numThreads = 10;
        int iteration = 20;
        AtomicInteger atomicInteger = new AtomicInteger();
        for (int b = 0; b < numThreads; b++) {
            new Thread(() -> {
                for (int a = 0; a < iteration; a++) {
                    atomicInteger.incrementAndGet();
                }
            }).start();
        }
    }

}
